(function (window) {
    /**
     * 公共基础方法
     */
    if(window.webviewJSbridge) return false;

    var md5 = require('./libs/md5');
    // 每次部署、发布时需要修改
    var VERSION = '1.1.0.0';//sdk版本号

    var WEB_SELECTION = false; //Web 圈选开关
    var S2_TIME = 30 * 60 * 1000;//有效期30分钟
    var CO_TIME = 3 * 365 * 24 * 60 * 60 * 1000;// 有效期三年
    var GEO_INFO = {};  //地理位置信息
    FW_STATE = 'uninstall';
    // var env = window.dev ? 'dev' : 'prd'; //dev, prd
    //全局配置
    var globalObject = {
        env : 'prd',//dev, prd 开发模式 生产模式
        platform : 'client'//client, web 控制日志参数格式
    };
    //获取当前页面的参数信息
    var location = {
        absUrl: window.location.href,
        referUrl: document.referrer,
        domain: window.location.hostname,
        port: window.location.port,
        path: window.location.pathname,
        search: window.location.search ? window.location.search.substring(1) : '',
        hash: window.location.hash,
        title: document.getElementsByTagName("title")[0].innerHTML,
        userAgent: navigator.userAgent
    };
    // api接口 分别对应https or http
    var reportApi = ('https:' == document.location.protocol ? 'https://sdkstatic.51kupai.com/' : 'http://sdkstatic.hiwemeet.com/');
    // 开发环境设置
    var envConfig = {
        dev: {
            debug: true,
            api: reportApi + 'sdkoff'
        },
        prd: {
            debug: false,
            api: reportApi + 'sdkon'
        }
    };
    // 开发环境，打印日志
    var logger = {
        // object to string
        printInfo: function (obj) {
            if (!envConfig[globalObject.env].debug) return;
            // console.log(obj);
        }
    };

    // cookie读写
    var cookies = {
        set: function (key, expTime) {
            var exp = new Date();
            exp.setTime(exp.getTime() + expTime);
            //var random = parseInt(Math.random() * 1000000);
            var random = util.generateMixed(6);
            var value = exp.getTime() + '_' + random;
            document.cookie = key + '=' + value + ';expires=' + exp.toGMTString() + ';path=/';
        },
        /**
         * @desc Get the value of given cookie key
         * @param key {String} Id to use for lookup
         * @return {String} Return the key of value
         */
        get: function (key) {
            //
            var arr, reg = new RegExp("(^| )" + key + "=([^;]*)(;|$)");
            if (arr = document.cookie.match(reg)) return unescape(arr[2]);
            else return '';
        },
        /**
         * @desc Get the deserialized value of given cookie key
         * @param key {String} Id to use for lookup
         * @return {object} Return the key of value
         */
        getObject: function (key) {
            //
        },
        /**
         * @desc Returns a key value object with all the cookies
         * @return {object} All cookies
         */
        getAll: function () {
            //
        },
        /**
         * @desc Sets a value for given cookie key
         * @param key {String} Id for the value
         * @param value {String} Raw value to be stored.
         * @param value {Object} optional Options object
         * @return void
         */
        put: function (key, value, options) {
            //
        },
        /**
         * @desc Serializes and sets a value for given cookie key
         * @param key {String} Id for the value
         * @param value {String} Raw value to be stored.
         * @param value {Object} optional Options object
         * @return void
         */
        putObject: function (key, value, options) {
            //
        },
        /**
         * @desc Remove given cookie
         * @param key {String} Id of the key-value pair to delete.
         * @param value {Object} optional Options object
         * @return void
         */
        remove: function () {
            //
        },

        isCookieExisted: function (key) {
            var val = this.get(key);
            var ret = false;
            if (!!val) {
                ret = true;
            }
            return ret;
        }
    };

    // event listener function
    var eventListener = {
        /**
         * @desc add listener IE and chrome
         * @param node {Object} document node element
         * @param type {String} event type name
         * @param handler {Function} callback method
         * @return {Boolean} true or false
         */
        addEvent: function (node, type, handler) {
            if (!node) return false;
            if (node.addEventListener) {
                node.addEventListener(type, handler, false); // 所有主流浏览器，除了 IE 8 及更早 IE版本
                return true;
            }
            else if (node.attachEvent) {
                node['e' + type + handler] = handler;
                node[type + handler] = function () {
                    node['e' + type + handler](window.event);
                };
                node.attachEvent('on' + type, node[type + handler]); // IE 8 及更早 IE 版本
                return true;
            }
            return false;
        },
        /**
         * @desc remove listener IE and chrome
         * @param node {Object} document node element
         * @param type {String} event type name
         * @param handler {Function} callback method
         * @return {Boolean} true or false
         */
        removeEvent: function (node, type, handler) {
            if (!node) return false;
            if (node.removeEventListener) {
                node.removeEventListener(type, handler, false);
                return true;
            }
            else if (node.detachEvent) {
                node.detachEvent('on' + type, node[type + handler]);
                node[type + handler] = null;
            }
            return false;
        },
        /**
         * @desc mobile touch event
         * @param node {Object} document node element
         * @param type {String} event type name
         * @param handler {Function} callback method
         * @return void
         */
        touchEvent: function (obj, type, func) {
            //滑动范围在5x5内则做点击处理，s是开始，e是结束
            var init = {x: 5, y: 5, sx: 0, sy: 0, ex: 0, ey: 0};
            var sTime = 0, eTime = 0;
            type = type.toLowerCase();

            obj.addEventListener("touchstart", function () {
                sTime = new Date().getTime();
                init.sx = event.targetTouches[0].pageX;
                init.sy = event.targetTouches[0].pageY;
                init.ex = init.sx;
                init.ey = init.sy;
                if (type.indexOf("start") != -1) func();
            }, false);

            obj.addEventListener("touchmove", function () {
                event.preventDefault();//阻止触摸时浏览器的缩放、滚动条滚动
                init.ex = event.targetTouches[0].pageX;
                init.ey = event.targetTouches[0].pageY;
                if (type.indexOf("move") != -1) func();
            }, false);

            obj.addEventListener("touchend", function () {
                var changeX = init.sx - init.ex;
                var changeY = init.sy - init.ey;
                if (Math.abs(changeX) > Math.abs(changeY) && Math.abs(changeY) > init.y) {
                    //左右事件
                    if (changeX > 0) {
                        if (type.indexOf("left") != -1) func();
                    } else {
                        if (type.indexOf("right") != -1) func();
                    }
                }
                else if (Math.abs(changeY) > Math.abs(changeX) && Math.abs(changeX) > init.x) {
                    //上下事件
                    if (changeY > 0) {
                        if (type.indexOf("top") != -1) func();
                    } else {
                        if (type.indexOf("down") != -1) func();
                    }
                }
                else if (Math.abs(changeX) < init.x && Math.abs(changeY) < init.y) {
                    eTime = new Date().getTime();
                    //点击事件，此处根据时间差细分下
                    if ((eTime - sTime) > 300) {
                        if (type.indexOf("long") != -1) func(); //长按
                    }
                    else {
                        if (type.indexOf("click") != -1) func(); //当点击处理
                    }
                }
                if (type.indexOf("end") != -1) func();
            }, false);
        }
    };

    //JS http ajax request
    var httpRequest = {
        /**
         * @desc ajax http request
         * @param type {String} post get put delete method
         * @param url {String} api url
         * @param data {Object} post data, get method without this param
         * @param success {Function} callback when success
         * @param failed {Function} callback when failed
         * @return void
         */
        ajax: function (type, url, data, success, failed) {
            // 创建ajax对象
            var xhr = null;
            if (window.XMLHttpRequest) {
                xhr = new XMLHttpRequest();
            } else {
                xhr = new ActiveXObject('Microsoft.XMLHTTP');
            }

            var type = type.toUpperCase();
            // 用于清除缓存
            var random = Math.random();

            if (typeof data == 'object') {
                var str = '';
                for (var key in data) {
                    str += key + '=' + data[key] + '&';
                }
                data = str.replace(/&$/, '');
            }

            if (type == 'GET') {
                if (data) {
                    xhr.open('GET', url + '?' + data, true);
                } else {
                    xhr.open('GET', url + '?t=' + random, true);
                }
                xhr.send();

            } else if (type == 'POST') {
                xhr.open('POST', url, true);
                // 如果需要像 html 表单那样 POST 数据，请使用 setRequestHeader() 来添加 http 头。
                xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
                xhr.send(data);
            }

            // 处理返回数据
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4) {
                    if (xhr.status == 200) {
                        success(xhr.responseText);
                    } else {
                        if (failed) {
                            failed(xhr.status);
                        }
                    }
                }
            }
        },
        /**
         * @desc load js script dynamically
         * @param url {src} api url
         * @param success {Function} callback when success
         * @return void
         */
        loadScript: function (src, callback) {
            var script = document.createElement('script'),
                head = document.getElementsByTagName('head')[0];
            script.type = 'text/javascript';
            script.charset = 'UTF-8';
            script.src = src;
            if (script.addEventListener) {
                script.addEventListener('load', function () {
                    callback();
                }, false);
            } else if (script.attachEvent) {
                script.attachEvent('onreadystatechange', function () {
                    var target = window.event.srcElement;
                    if (target.readyState == 'loaded') {
                        callback();
                    }
                });
            }
            head.appendChild(script);
        }
    };

    // geo information
    var geoInfo = {
        location: function () {
            httpRequest.loadScript('http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=jsonp', function () {
                var obj = {
                    country: remote_ip_info.country,
                    province: remote_ip_info.province,
                    city: remote_ip_info.city
                };
                GEO_INFO.country = obj.country;
                GEO_INFO.province = obj.province;
                GEO_INFO.city = obj.city;
                logger.printInfo(obj);
            });
        },
        ipAddress: function () {
            httpRequest.loadScript('http://www.coding123.net/getip.ashx?js=1', function () {
                var obj = {
                    ipv4: ip.substr(1)
                };
                GEO_INFO.ipv4 = obj.ipv4;
                logger.printInfo(obj);
            });
        }
    };

    //获取当前点击时间
    var dateTime = {
        formatTime: function () {
//
            Date.prototype.Format = function (fmt) { //author: meizz
                var o = {
                    "M+": this.getMonth() + 1, //月份
                    "d+": this.getDate(), //日
                    "h+": this.getHours(), //小时
                    "m+": this.getMinutes(), //分
                    "s+": this.getSeconds(), //秒
                    "q+": Math.floor((this.getMonth() + 3) / 3), //季度
                    "S": this.getMilliseconds() //毫秒
                };
                if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
                for (var k in o)
                    if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
                return fmt;
            };

            //var time1 = new Date().Format("yyyy-MM-dd");
            var time = new Date().Format("yyyy-MM-dd hh:mm:ss");


            return time;
        },
        unixTime: function () {
            return new Date().getTime();
        }
    };


    // 设备操作系统判断
    var deviceDetector = {
        //判断当前浏览器的类型
        browser: function () {
            var OsObject = {};
            if (navigator.userAgent.indexOf("MSIE") > 0) {
                OsObject = {browser: 'IE'};
            }
            if (navigator.userAgent.indexOf("Chrome") > 0) {
                if (navigator.userAgent.indexOf("OPR/") > 0) {
                    OsObject = {browser: 'Opera'};
                } else if (navigator.userAgent.indexOf("BIDUBrowser") > 0) {
                    OsObject = {browser: 'baidu'};
                } else if (navigator.userAgent.indexOf("UBrowser") > 0) {
                    OsObject = {browser: 'UC'};
                } else if (navigator.userAgent.indexOf("QQBrowser") > 0) {
                    OsObject = {browser: 'QQBrowser'};
                } else if (navigator.userAgent.indexOf("2345Explorer") > 0) {
                    OsObject = {browser: '2345Explorer'};
                } else {
                    OsObject = {browser: 'Chrome'};
                }
            } else if (navigator.userAgent.indexOf("Safari") > 0) {
                OsObject = {browser: 'Safari'};
            }
            if (navigator.userAgent.indexOf("Firefox") > 0) {
                OsObject = {browser: 'Firefox'};
            }
            logger.printInfo(OsObject);
            return OsObject;
        },
        os: function () {
            var osData = {};
            var sUserAgent = navigator.userAgent;
            var isWin = (navigator.platform == "Win32") || (navigator.platform == "Windows");
            var isMac = (navigator.platform == "Mac68K") || (navigator.platform == "MacPPC") || (navigator.platform == "Macintosh") || (navigator.platform == "MacIntel");
            if (isMac) {
                osData.os = 'macOS';
            }
            var isUnix = (navigator.platform == "X11") && !isWin && !isMac;
            if (isUnix) {
                osData.os = 'Unix';
            }
            var isLinux = (String(navigator.platform).indexOf("Linux") > -1);
            if (isLinux) {
                osData.os = 'Linux';
            }
            if (isWin) {
                var isWin2K = sUserAgent.indexOf("Windows NT 5.0") > -1 || sUserAgent.indexOf("Windows 2000") > -1;
                if (isWin2K) {
                    osData.os = 'Win2000';
                }
                var isWinXP = sUserAgent.indexOf("Windows NT 5.1") > -1 || sUserAgent.indexOf("Windows XP") > -1;
                if (isWinXP) {
                    osData.os = 'WinXP';
                }
                var isWin2003 = sUserAgent.indexOf("Windows NT 5.2") > -1 || sUserAgent.indexOf("Windows 2003") > -1;
                if (isWin2003) {
                    osData.os = 'Win2003';
                }
                var isWinVista = sUserAgent.indexOf("Windows NT 6.0") > -1 || sUserAgent.indexOf("Windows Vista") > -1;
                if (isWinVista) {
                    osData.os = 'WinVista';
                }
                var isWin7 = sUserAgent.indexOf("Windows NT 6.1") > -1 || sUserAgent.indexOf("Windows 7") > -1;
                if (isWin7) {
                    osData.os = 'Win7';
                }
                var isWin8 = sUserAgent.indexOf("Windows NT 8.1") > -1 || sUserAgent.indexOf("Windows 8") > -1;
                if (isWin8) {
                    osData.os = 'Win8';
                }
                var isWin10 = sUserAgent.indexOf("Windows NT 10.0") > -1 || sUserAgent.indexOf("Windows 10") > -1;
                if (isWin10) {
                    osData.os = 'Win10';
                }
            }
            if (sUserAgent.indexOf('Android') > -1 || sUserAgent.indexOf('Linux') > -1) {//安卓手机
                osData.os = 'Android';
            } else if (sUserAgent.indexOf('iPhone') > -1) {//苹果手机
                osData.os = 'iOS';
            } else if (sUserAgent.indexOf('Windows Phone') > -1) {//winphone手机
                osData.os = 'Windows';
            }
            logger.printInfo(osData);
            return osData;
        }
    };

    //工具函数
    var util = {
        /**
         * Is #obj empty?
         * @param {Object} obj object to be detected.
         */
        isEmptyObject: function (obj) {
            for (var prop in obj) {
                if (obj.hasOwnProperty(prop)) {
                    return false;
                }
            }
            return true;
        },

        /**
         * @desc  获取随机固定位数的随机字符串
         * @param length {Number} 返回的随机字符串位数
         * @return {String} 随机字符串
         */
        generateMixed: function (length) {
            var chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
            var res = "";
            for (var i = 0; i < length; i++) {
                var id = Math.ceil(Math.random() * 35);
                res += chars[id];
            }
            return res;
        },
        queryString: function () {
            // This function is anonymous, is executed immediately and
            // the return value is assigned to QueryString!
            var query_string = {};
            var query = window.location.search.substring(1);
            var vars = query.split("&");
            for (var i = 0; i < vars.length; i++) {
                var pair = vars[i].split("=");
                // If first entry with this name
                if (typeof query_string[pair[0]] === "undefined") {
                    query_string[pair[0]] = decodeURIComponent(pair[1]);
                    // If second entry with this name
                } else if (typeof query_string[pair[0]] === "string") {
                    var arr = [query_string[pair[0]], decodeURIComponent(pair[1])];
                    query_string[pair[0]] = arr;
                    // If third or later entry with this name
                } else {
                    query_string[pair[0]].push(decodeURIComponent(pair[1]));
                }
            }
            return query_string;
        },

        getXPath: function (element, flag) {
            if (element == undefined || element.nodeName == '#document' || element.tagName.toLocaleLowerCase() == 'html') {
                return '';
            }
            var parentElement = element.parentNode ? element.parentNode : element.parentElement;
            var eleList = parentElement.getElementsByTagName(element.tagName);
            var obj = {};
            for (var i = 0; i < eleList.length; i++) {
                if (eleList[i].innerHTML == element.innerHTML) {
                    obj.tagName = element.tagName.toLocaleLowerCase();
                    obj.index = i;
                    obj.id = element.id;
                    obj.className = element.className;
                }
            }
            var str = obj.tagName;
            var path = util.getXPath(parentElement, true) + str + '[' + obj.index + ']';
            if (flag) { //当前元素的末尾不加'/'
                path += '/';
            }
            return path;
        },

        getAllXpath: function (xpath) {
            var ret = [];
            var arr = [];
            var currentXpath = '';
            var list = xpath.split('/');
            for (var i = 0; i < list.length; i++) {
                if (!list[i]) return;
                arr.push(list[i]);
                currentXpath = arr.join('/');
                ret.push(md5(currentXpath));
                //ret.push(currentXpath);
            }
            // 保留三层
            //return ret.slice(ret.length - 3, ret.length);
            return ret;
        },

        // 保留三层 Xpath
        get3LayersXpath: function (allXpathArr) {
            var ret = allXpathArr || [];
            if (allXpathArr.length > 3) {
                ret = allXpathArr.slice(allXpathArr.length - 3, allXpathArr.length);
            }
            return ret;
        },
        //控件类型 Map
        elementTypeMap: function (tagName, type) {
            tagName = tagName.toLocaleLowerCase();
            if (type) {
                tagName = tagName + '-' + type.toLocaleLowerCase();
            }
            var ret = 12; //自定义
            switch (tagName) {
                case 'button':
                case 'a':
                    ret = 1;
                    break;
                case 'label':
                    ret = 2;
                    break;
                case 'img':
                    ret = 3;
                    break;
                case 'li':
                    ret = 6;
                    break;
                case 'td':
                    ret = 7;
                    break;
                case 'input-radio':
                    ret = 8;
                    break;
                case 'input-checkbox':
                    ret = 9;
                    break;
                case 'select':
                    ret = 15;
                    break;
            }
            return ret;
        },
        extend:function (obja, objb) {
            if(!this.isObject(obja)){
                return objb;
            }else if(!this.isObject(objb)){
                return obja;
            }else{
                for(var i in objb){
                    if(objb[i] === undefined) continue;
                    obja[i] = objb[i];
                }
                return obja;
            }
        },
        isObject:function(val){
            return val.constructor === Object
        }
    };

    /*-------------- Business Logic ---------------------------------------------------------------------------------*/

    var addListenerForTags = function () {
        var handler = function (event) {
            //IE only support srcElement, firefox only support target
            var eventTarget = event.srcElement ? event.srcElement : event.target;
            if (WEB_SELECTION) {
                handlerWebSelection(event, eventTarget);
            } else if ('touchstart' === event.type || 'click' === event.type) {
                handlerWebCrawler(event, eventTarget);
            }
        };
        var handlerWebCrawler = function (event, eventTarget) {
            var obj = {
                innerHTML: eventTarget.innerHTML, // contains html tags
                innerText: eventTarget.innerText.replace(/[\s]/g, " "), // only text content
                clientX: event.clientX,
                clientY: event.clientY,
                screenX: event.screenX,
                screenY: event.screenY,
                eventType: event.type, // event type, e.g. click, mouseOver etc.
                tagName: eventTarget.tagName, // html tags
                title: eventTarget.title,
                id: eventTarget.id,
                className: eventTarget.className,
                baseURI: eventTarget.baseURI, // absolute url
                parentNode: event.parentNode, // parent dom element
                attributes: event.attributes, // all of attributes with current html tag, {Array}
                path: event.path // all of parent node dom elements, {Array},
            };

            // handler path node
            var pathArr = [];
            if (obj.path) {
                for (var i = 0; i < obj.path.length; i++) {
                    var pathObj = {};
                    var pathEventObj = obj.path[i];
                    if (pathEventObj.tagName) {
                        pathObj.tagName = pathEventObj.tagName;
                        if (!util.isEmptyObject(pathObj)) {
                            pathArr.push(pathObj);
                        }
                    }
                }
            }
            obj.path = pathArr;

            // get xpath td[@id='tableEnd' and @class='table tr5 td4'][3]/tr[4]/tbody[0]/table[0]/div[2]/
            obj.xpath = util.getXPath(eventTarget);

            // form input
            if ('input' === eventTarget.tagName.toLocaleLowerCase()) {
                obj.inputType = eventTarget.type; // for input tag, type attr
                obj.value = eventTarget.value; // for input tag, type attr
                obj.name = eventTarget.name; // for input tag, type attr
            }

            // select
            if ('select' === eventTarget.tagName.toLocaleLowerCase()) {
                obj.selectedIndex = eventTarget.selectedIndex;
                var selectedOptions = [];
                var arrays = eventTarget.selectedOptions;
                for (var i = 0; i < arrays.length; i++) {
                    var o = {
                        innerHTML: arrays[i].innerHTML,
                        tagName: arrays[i].tagName,
                        value: arrays[i].value,
                        index: arrays[i].index
                    };
                    selectedOptions.push(o);
                }
                obj.selectedOptions = selectedOptions;
            }
            // table
            if ('td' === eventTarget.tagName.toLocaleLowerCase() ||
                'li' === eventTarget.tagName.toLocaleLowerCase()) {
                obj.rowIndex = eventTarget.parentNode.rowIndex; //当前单元格行值
                obj.cellIndex = eventTarget.cellIndex; //当前单元格列值
                obj.rows = eventTarget.parentNode.parentNode.childElementCount; //该表总行数
                obj.columns = eventTarget.parentNode.childElementCount; //该表总列数
            }
            // img
            if ('img' === eventTarget.tagName.toLocaleLowerCase()) {
                obj.width = eventTarget.width;
                obj.height = eventTarget.height;
                obj.alt = eventTarget.alt;
                obj.src = eventTarget.src;
            }
            // a link
            if ('a' === eventTarget.tagName.toLocaleLowerCase()) {
                obj.href = eventTarget.href;
                obj.rel = eventTarget.rel;
                obj.target = eventTarget.target;
            }
            //父级
            var parentElement = eventTarget.parentNode;
            obj.pXpath = util.getXPath(parentElement);
            obj.pInnerHTML = parentElement.innerHTML;
            obj.pInnerText = parentElement.innerText;
            obj.pClientX = parentElement.clientX;
            obj.pClientY = parentElement.clientY;
            obj.pTagName = parentElement.tagName;
            obj.pInputType = parentElement.type;
            obj.pHref = parentElement.href;
            //祖父级
            var grandpaElement = parentElement.parentNode;
            obj.gpXpath = util.getXPath(grandpaElement);
            obj.gpInnerHTML = grandpaElement.innerHTML;
            obj.gpInnerText = grandpaElement.innerText;
            obj.gpClientX = grandpaElement.clientX;
            obj.gpClientY = grandpaElement.clientY;
            obj.gpTagName = grandpaElement.tagName;
            obj.gpInputType = grandpaElement.type;
            obj.gpHref = grandpaElement.href;
            // send ajax request to backend
            sendRequest(obj);
            //logger.printInfo(obj);
        };

        var handlerWebSelection = function (event, eventTarget) {
            if ('mouseover' === event.type) {
                eventTarget.classList.add('circle-hover')
            } else if ('mouseout' === event.type) {
                eventTarget.classList.remove('circle-hover')
            }
            //阻止链接的的默认行为
            event.preventDefault();
            return false;
        };
        var sendRequest = function (object) {
            //data format by requirement
            // event type map
            if (!cookies.isCookieExisted('s2')) {
                cookies.set('s2', S2_TIME);
            }
            if (!cookies.isCookieExisted('co')) {
                cookies.set('co', CO_TIME);
            }
            var osInfo = deviceDetector.os();
            var xpathArr = util.get3LayersXpath(util.getAllXpath(object.xpath));
            var eventId = md5(object.xpath); //event ID format: path_text
            if (object.innerText) {
                eventId = md5(object.xpath + '_' + object.innerText);
            }

            var dataObj = {};
            // 客户端日志规范
            dataObj.client = {
                client_event: [
                    {
                        v: eventId,
                        a: globalObject.appId || clientOptions.a, // app ID
                        b: clientOptions.b, // app version
                        c: clientOptions.c, // channel ID
                        d: clientOptions.d, // uid
                        e: clientOptions.e, // device ID
                        g: clientOptions.g, // Android/iOS
                        j: clientOptions.j, // Android/iOS
                        t1: dateTime.unixTime(), //进入p1客户端时间
                        w: eventTypeMap(object.eventType), //option type by user
                        x1: object.clientX,
                        y1: object.clientY,
                        y: object.innerText || '', //contains html text
                        y2: object.xpath, //current tag path depth
                        z: util.elementTypeMap(object.tagName, object.inputType), //z=1按钮; z=2文字; z=3图片; map, refer to html tag and input type
                        y3: xpathArr.join('/'),
                        y4: location.absUrl,
                        u: VERSION, // sdk版本号
                        s2: clientOptions.s2,
                        w2: 1 // w2=1表示应用内H5 页面；w2=0标识客户端原生页面
                    }
                ]
            };
            // Web端日志规范
            dataObj.web = {
                wevent: [
                    {
                        v: eventId, // event ID format: path_text
                        a: globalObject.appId || '', //app ID
                        wt: globalObject.wt || '', // app version
                        d: '', //uid TODO
                        g: osInfo.os, //Android/iOS TODO
                        t1: dateTime.unixTime(), //进入p1客户端时间
                        w: eventTypeMap(object.eventType), //option type by user
                        x1: object.clientX,
                        y1: object.clientY,
                        y: object.innerText, //contains html text
                        y2: object.xpath, //current tag path depth
                        z: util.elementTypeMap(object.tagName, object.inputType), //z=1按钮; z=2文字; z=3图片; map, refer to html tag and input type
                        y3: xpathArr.join('/'),
                        y4: location.absUrl,
                        u: VERSION, // sdk版本
                        s2: cookies.get('s2'), // 会话ID
                        co: cookies.get('co'), // 识别用户
                        p: location.referUrl,
                        do: location.domain,
                        po: location.port,
                        pa: location.path,
                        ht: location.hash,
                        qu: location.search,
                        ti: location.title,
                        ua: location.userAgent,
                        w2: 2, // w2=1表示应用内H5 页面；w2=0标识客户端原生页面
                        hr: object.href || ''
                    }
                ]
            };

            sendHttpRequest(dataObj);

            // 事件类型 Map
            function eventTypeMap(eventType) {
                eventType = eventType.toLocaleLowerCase();
                var ret = 1; //自定义
                switch (eventType) {
                    case 'click':
                        ret = 1;
                        break;
                    case 'press':
                        ret = 2;
                        break;
                    case 'top':
                        ret = 3;
                        break;
                    case 'bottom':
                        ret = 4;
                        break;
                    case 'left':
                        ret = 5;
                        break;
                    case 'right':
                        ret = 6;
                        break;
                }
                return ret;
            }
        };
        if(globalObject.platform === 'client'){
            eventListener.addEvent(document.body, 'touchstart', handler);
        }else{
            eventListener.addEvent(document.body, 'click', handler);
        }
        // eventListener.addEvent(document.body, 'mouseover', handler);
        // eventListener.addEvent(document.body, 'mouseout', handler);

    };


    function sendPagePathEvent() {
        var osInfo = deviceDetector.os();
        var queryObj = util.queryString();
        /* start log */
        var timeStamp = dateTime.unixTime();
        if (!location.referUrl) {
            sessionStorage.removeItem('t1');
        }

        var dataObj = {};
        /*移动端内嵌*/
        dataObj.client = {
            pagepath: [
                {
                    a: globalObject.appId || clientOptions.a, // app ID
                    b: clientOptions.b, // app version
                    c: clientOptions.c, // channel ID
                    d: clientOptions.d, // uid
                    e: clientOptions.e, // device ID
                    g: clientOptions.g, // Android/iOS
                    j: clientOptions.j, // Android/iOS
                    t1: clientOptions.t || parseInt(sessionStorage.getItem('t1')) || '', // 进入p2客户端时间 TODO 格式
                    t2: timeStamp, // 进入p2客户端时间
                    p: clientOptions.p || location.referUrl, // p1页面名称（使用控件名）
                    q: location.absUrl, // p2页面名称（使用控件名）
                    u: VERSION, // sdk版本号
                    s2: clientOptions.s2, // 会话ID
                    w2: 1 // w2=1表示应用内H5 页面；w2=0标识客户端原生页面
                }
            ]
        };
        /*web端嵌入*/
        dataObj.web = {
            wpagepath: [
                {
                    a: globalObject.appId || '', // app ID TODO
                    wt: globalObject.wt || '', //TODO
                    d: '', // uid TODO,
                    g: osInfo.os, // Android/iOS
                    t1: parseInt(sessionStorage.getItem('t1')) || '', // 进入p2客户端时间 TODO 格式
                    t2: timeStamp, // 当前页面的时间
                    p: location.referUrl, // p1页面名称（使用控件名）
                    q: location.absUrl, // p2页面名称（使用控件名）
                    u: VERSION, // sdk版本号
                    s2: cookies.get('s2'), // 会话ID
                    co: cookies.get('co'), // 识别用户
                    do: location.domain,
                    po: location.port,
                    pa: location.path,
                    ht: location.hash,
                    qu: location.search,
                    ti: location.title,
                    so: location.referUrl,
                    ua: location.userAgent,
                    w2: 2, // w2=1表示应用内H5 页面；w2=0标识客户端原生页面
                    ai: queryObj.ai || '', //ID 自定义，当前时间戳
                    as: queryObj.as || '',
                    am: queryObj.am || '',
                    an: queryObj.an || '',
                    ac: queryObj.ac || '',
                    at: queryObj.at || ''
                }
            ]
        };
        sessionStorage.setItem('t1', timeStamp);

        sendHttpRequest(dataObj);
    }

    //设备字段
    // a appid
    // b app version
    // c channel id
    // d uid
    // e device id
    // g os
    // s2 会话id
    // j 广告id
    var clientOptions = {a: '', b: '', c: '', d: '', e: '', g: '', s2: '', j: ''};
    var jsBridge = window.webviewJSbridge = {};

    /*
     *  外部接口函数
     *  手动埋点
     */
    jsBridge.setEventU = function (jsonObj) {
        //appId, wt, uid, eventId, href, expand
        jsonObj = jsonObj || {};
        var osInfo = deviceDetector.os();

        var dataObj = {};
        dataObj.client = {
            client_eventu: [
                {
                    v1: jsonObj.eventId || '',
                    a: globalObject.appId || clientOptions.a, //app ID
                    b: clientOptions.b,
                    c: clientOptions.c,
                    d: clientOptions.d, //uid
                    e: clientOptions.e,
                    g: clientOptions.g, //Android/iOS
                    j: clientOptions.j, // Android/iOS
                    t1: dateTime.unixTime(),
                    x: jsonObj.expand || {},
                    u: VERSION, // sdk版本号
                    s2: clientOptions.s2, //会话ID
                    y4: location.absUrl,
                    w2: 1
                }]
        };

        dataObj.web = {
            weventu: [
                {
                    a: globalObject.appId || '', //app ID
                    wt: globalObject.wt || '', //wt=0 官网; wt=1推广链接
                    s2: cookies.get('s2'), //会话ID
                    d: jsonObj.uid || '', //uid
                    g: osInfo.os, //Android/iOS
                    v1: jsonObj.eventId || '',
                    do: location.domain,
                    po: location.port,
                    pa: location.path,
                    ht: location.hash,
                    p: location.referUrl, // p1页面名称（使用控件名）
                    q: location.absUrl,
                    qu: location.search,
                    co: cookies.get('co'), // 识别用户
                    ti: location.title,
                    hr: jsonObj.href || '',
                    x: jsonObj.expand || {},
                    u: VERSION, // sdk版本号
                    t1: dateTime.unixTime(),
                    ua: location.userAgent
                }]
        };

        sendHttpRequest(dataObj);
    };

    // 接口函数，给ios／anndroid 使用
    function getTargetElement(x, y) {

        var targetElement = document.getElementsByTagName('body')[0];

        searchElement(document.getElementsByTagName('body')[0]);

        function searchElement(elmt) {
            var child = elmt.childNodes;
            for (var i = 0; i < child.length; i++) {
                if (child[i].nodeName != '#text' && child[i].nodeName != '#comment') {

                    var X = child[i].getBoundingClientRect().left + document.documentElement.scrollLeft;
                    var Y = child[i].getBoundingClientRect().top + document.documentElement.scrollTop;
                    var W = child[i].clientWidth;
                    var H = child[i].clientHeight;

                    if (X <= x && Y <= y && X + W > x && Y + H > y) {
                        targetElement = child[i];
                        searchElement(child[i]);
                    }
                }
            }
        }

        return targetElement;
    }

    jsBridge.getTargetElementForIOSMove = function (x, y) {
        var targetElement = getTargetElement(x, y);
        var ret = {
            x: targetElement.getBoundingClientRect().left + document.documentElement.scrollLeft,
            y: targetElement.getBoundingClientRect().top + document.documentElement.scrollTop,
            w: targetElement.clientWidth,
            h: targetElement.clientHeight
        };
        return JSON.stringify(ret);
    };

    jsBridge.getTargetElementForIOSEnd = function (x, y) {
        var ret = getTargetElement(x, y);
        var xpath = util.getXPath(ret);
        var allXpath = util.get3LayersXpath(util.getAllXpath(xpath));
        var viewArray = [];
        var n = 0;

        function getViewArray(dom) {
            if(!dom || dom.nodeName =='#document') {
                return;
            }
            if (n < 3) {
                var _obj = {
                    x: dom.getBoundingClientRect().left + document.documentElement.scrollLeft,
                    y: dom.getBoundingClientRect().top + document.documentElement.scrollTop,
                    w: dom.clientWidth,
                    h: dom.clientHeight,
                    ID: md5(util.getXPath(dom))
                };
                viewArray.push(_obj);
                n++;
                getViewArray(dom.parentNode);
            }
        }

        getViewArray(ret);

        var obj = {
            widgets: allXpath.join('/'),
            viewArray: viewArray
        };
        var limitedTextID = md5(xpath + '_' + ret.innerText);
        var unLimitedTextID = md5(xpath);
        obj.isContainText = (ret.innerText == '') ? 0 : 1;
        obj.unLimitedTextID = unLimitedTextID;
        obj.limitedTextID = limitedTextID;
        obj.text = ret.innerText;
        return JSON.stringify(obj);
    };

    //var cycenter_bridge = cycenter_bridge || null;
    jsBridge.getTargetElementForAndroid = function (x, y) {
        var ret = getTargetElement(x, y);
        var xpath = util.getXPath(ret);
        var allXpath = util.get3LayersXpath(util.getAllXpath(xpath));

        var viewArray = [];
        var n = 0;

        function getViewArray(dom) {
            if (n < 3) {
                var _obj = {
                    path: util.getXPath(dom),
                    ex: dom.getBoundingClientRect().left + document.documentElement.scrollLeft,
                    ey: dom.getBoundingClientRect().top + document.documentElement.scrollTop,
                    ew: dom.offsetWidth,
                    eh: dom.offsetHeight,
                    nodeType: util.elementTypeMap(dom.tagName, dom.inputType),
                    v: md5(util.getXPath(dom))
                };
                if (n == 0) {
                    _obj.c = dom.innerText;
                    if (dom.tagName.toLocaleLowerCase() == 'img') {
                        _obj.h = dom.src;
                        var fileName = _obj.h.split("/");
                        _obj.c = fileName[fileName.length - 1];
                    } else if (dom.innerText != '') {
                        _obj.v = md5(util.getXPath(dom) + '_' + dom.innerText);
                    }
                }
                viewArray.push(_obj);
                n++;
                getViewArray(dom.parentNode);
            }
        }

        getViewArray(ret);

        var host = window.location.host;
        var href = window.location.href;
        var page = href.substring(href.indexOf(host) + host.length, href.length);
        var obj = {
            d: host,
            p: page,
            w: allXpath.join('/'),
            e: viewArray
        };
        logger.printInfo(obj);
        if (cycenter_bridge) {
            cycenter_bridge.saveEvent(JSON.stringify(obj));
        }else{
            return JSON.stringify(obj);
        }
    };

    jsBridge.getTargetElementForAndroidHover = function (x, y) {
        var targetElement = getTargetElement(x, y);
        if (!targetElement) return;
        var ret = {
            ex: targetElement.getBoundingClientRect().left + document.documentElement.scrollLeft,
            ey: targetElement.getBoundingClientRect().top + document.documentElement.scrollTop,
            ew: targetElement.offsetWidth,
            eh: targetElement.offsetHeight
        };
        if (cycenter_bridge) {
            cycenter_bridge.hoverNodes(JSON.stringify(ret));
        }else{
            return JSON.stringify(ret);
        }
    };

    var getEjsObject = function () {
        var ret = {
        };
        for (var i = 0; i < _ejs.length; i++) {
            var key = _ejs[i][0];
            var value = _ejs[i][1];
            if(key.toLowerCase() == 'env'){
                if(value.toString() == 'true'){
                    ret['env'] = 'dev';
                }else{
                    ret['env'] = 'prd';
                }
                continue;
            }else if(key.toLowerCase() == 'platform' && value != 'web'){
                continue;
            }
            ret[key] = _ejs[i][1];
        }
        return ret;
    };

    var isClientParamsPassed = function () {
        var ret = true;
        if (globalObject.platform === 'client' &&
            clientOptions.a != '' &&
            clientOptions.c != '' &&
            clientOptions.d != '') {
            ret = true;
        }
        return ret;
    };

    function sendHttpRequest(dataObj) {
        var postData = {};
        if (globalObject.platform === 'client') {
            postData = dataObj.client;
            //客户端模式下，pagepath交给客户端发送，jssdk取消发送
            if(postData.pagepath) return;
            if(postData.pagepath && postData.pagepath[0].p == postData.pagepath[0].q) return;
            if (!isClientParamsPassed()) return; //对客户端H5，基础参数iosOptions没有值，不上报日志
        }
        if (globalObject.platform === 'web') {
            postData = dataObj.web;
        }
        var onSuccess = function (res) {
            logger.printInfo(res);
        };
        var onFailure = function (status) {
            logger.printInfo(status);
        };
        httpRequest.ajax('post', envConfig[globalObject.env].api, JSON.stringify(postData), onSuccess, onFailure);
        logger.printInfo(postData);
    }


    /*
     *  initialize globalObject
     */
    globalObject = util.extend(globalObject, getEjsObject());

    /*
     *  application entry, initialization
     */

    //获取系统参数-初始化函数
    jsBridge.getAppOptions = function (jsonData) {
        if (jsonData) {
            clientOptions = jsonData;
        }
        // var param = {p: location.absUrl, t: dateTime.unixTime()};
        // sendPagePathEvent();

        if(globalObject.platform == 'client'){
            init();
        }
        var cycenter_bridge = window.cycenter_bridge;
        if (cycenter_bridge) {
            cycenter_bridge.pageChange(JSON.stringify(jsonData));
        }else{
            return JSON.stringify(jsonData);
        }
    };
    var init = function(){
        if (!cookies.isCookieExisted('s2')) {
            cookies.set('s2', S2_TIME);
        }
        if (!cookies.isCookieExisted('co')) {
            cookies.set('co', CO_TIME);
        }
        if(FW_STATE == 'uninstall'){
            addListenerForTags();
            FW_STATE = 'ready';
            return;
        }
        //geoInfo.location();
        //geoInfo.ipAddress();
        // if(globalObject.platform == 'web') {
        //     sendPagePathEvent();
        // }
    };

    //客户端自动注入，客户端调用初始化
    if(globalObject.platform == 'web'){
        //web端按照在load事件出发时初始化。
        eventListener.addEvent(window, 'load', init);
        sendPagePathEvent();
    }

})(window);